﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using System.Xml;
using System.Xml.Serialization;
using ERDDiagram.CrmSdk;
using ERDDiagram.helpers;
using ERDDiagram.Model;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;

using SD.Shapes.Common;
using SD.Shapes.Common.Connections;
using MEFedMVVM.Common;
using System.Diagnostics;
using System.Windows.Media;

namespace ERDDiagram.ViewModel
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm/getstarted
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        public bool CloseForm { get; set; }
        private readonly IDataService _dataService;
        // Public methods

        public void NodePositionChanged(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (SelectedDiagramItem is MyNode)
            {
                if (((MyNode)SelectedDiagramItem).Position.X < 10)
                {
                    LayoutrootWidth = LayoutrootWidth + 300;

                    ((MyNode)SelectedDiagramItem).Position = new Point(10, ((MyNode)SelectedDiagramItem).Position.Y);
                }
                else if (((MyNode)SelectedDiagramItem).Position.X > (((Grid)((Canvas)((Diagram)sender).Parent).Parent).ActualWidth - ((MyNode)SelectedDiagramItem).Width))
                {
                    LayoutrootWidth = LayoutrootWidth + 300;
                    ((MyNode)SelectedDiagramItem).Position = new Point(((MyNode)SelectedDiagramItem).Position.X - 10 -
                                                                                ((MyNode)SelectedDiagramItem).Width,
                                                                                    ((MyNode)SelectedDiagramItem).Position.Y);

                }

                if (((MyNode)SelectedDiagramItem).Position.Y < 10)
                {
                    LayoutrootHeight = LayoutrootHeight + 300;

                    ((MyNode)SelectedDiagramItem).Position = new Point(((MyNode)SelectedDiagramItem).Position.X, 10);
                }
                else if (((MyNode)SelectedDiagramItem).Position.Y > (((Grid)((Canvas)((Diagram)sender).Parent).Parent).ActualHeight - ((MyNode)SelectedDiagramItem).Height))
                {
                    LayoutrootHeight = LayoutrootHeight + 300;
                    ((MyNode)SelectedDiagramItem).Position = new Point(((MyNode)SelectedDiagramItem).Position.X,
                                                                                    ((MyNode)SelectedDiagramItem).Position.Y - ((MyNode)SelectedDiagramItem).Height - 10);

                }
                CheckForScrollBarsVisibility();
            }


        }


        /// <summary>
        /// The <see cref="LayoutrootWidth" /> property's name.
        /// </summary>
        public const string LayoutrootActualWidthPropertyName = "LayoutrootActualWidth";

        private double _LayoutrootActualWidth = 600;

        /// <summary>
        /// Sets and gets the LayoutrootWidth property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public double LayoutrootActualWidth
        {
            get
            {
                return _LayoutrootActualWidth;
            }

            set
            {
                if (_LayoutrootActualWidth == value)
                {
                    return;
                }

                _LayoutrootActualWidth = value;
                CheckForScrollBarsVisibility();
                RaisePropertyChanged(LayoutrootActualWidthPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="LayoutrootWidth" /> property's name.
        /// </summary>
        public const string LayoutrootWidthPropertyName = "LayoutrootWidth";

        private double _LayoutrootWidth = 600;

        /// <summary>
        /// Sets and gets the LayoutrootWidth property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public double LayoutrootWidth
        {
            get
            {
                return _LayoutrootWidth;
            }

            set
            {
                if (_LayoutrootWidth == value)
                {
                    return;
                }

                _LayoutrootWidth = value;
                RaisePropertyChanged(LayoutrootWidthPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="LayoutrootHeight" /> property's name.
        /// </summary>
        public const string LayoutrootActualHeightPropertyName = "LayoutrootActualHeight";

        private double _LayoutrootActualHeight = 600;

        /// <summary>
        /// Sets and gets the LayoutrootHeight property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public double LayoutrootActualHeight
        {
            get
            {
                return _LayoutrootActualHeight;
            }

            set
            {
                if (_LayoutrootActualHeight == value)
                {
                    return;
                }

                _LayoutrootActualHeight = value;
                CheckForScrollBarsVisibility();
                RaisePropertyChanged(LayoutrootActualHeightPropertyName);
            }
        }


        /// <summary>
        /// The <see cref="LayoutrootHeight" /> property's name.
        /// </summary>
        public const string LayoutrootHeightPropertyName = "LayoutrootHeight";

        private double _LayoutrootHeight = 600;

        /// <summary>
        /// Sets and gets the LayoutrootHeight property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public double LayoutrootHeight
        {
            get
            {
                return _LayoutrootHeight;
            }

            set
            {
                if (_LayoutrootHeight == value)
                {
                    return;
                }

                _LayoutrootHeight = value;
                RaisePropertyChanged(LayoutrootHeightPropertyName);
            }
        }

        private RelayCommand _resizeDiagram;

        /// <summary>
        /// Gets the ResizeDiagram.
        /// </summary>
        public RelayCommand ResizeDiagram
        {
            get
            {
                return _resizeDiagram
                    ?? (_resizeDiagram = new RelayCommand(
                                          () =>
                                          {
                                              MessageBox.Show("11");
                                          }));
            }
        }

        public Visibility WarningMessage
        {
            get
            {
                if (string.IsNullOrEmpty(DiagramID))
                {
                    return Visibility.Visible;
                }
                else
                {
                    return Visibility.Collapsed;
                }
            }
        }



        /// <summary>
        /// The <see cref="DiagramDisplay" /> property's name.
        /// </summary>
        public const string DiagramDisplayPropertyName = "DiagramDisplay";


        public Visibility DiagramDisplay
        {
            get
            {
                if (!string.IsNullOrEmpty(DiagramID))
                {
                    return Visibility.Visible;
                }
                else
                {
                    return Visibility.Collapsed;
                }
            }
        }
        /// <summary>
        /// The <see cref="DiagramID" /> property's name.
        /// </summary>
        public const string DiagramIDPropertyName = "DiagramID";

        private string _diagramid = "";

        /// <summary>
        /// Sets and gets the DiagramID property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string DiagramID
        {
            get
            {
                return _diagramid;
            }

            set
            {
                if (_diagramid == value)
                {
                    return;
                }

                _diagramid = value;
                RaisePropertyChanged(DiagramIDPropertyName);
            }
        }

        #region ZoomMEthods

        #region ZoomLessCommand
        private RelayCommand _ZoomLessCommand;

        /// <summary>
        /// Gets the ZoomLessCommand.
        /// </summary>
        public RelayCommand ZoomLessCommand
        {
            get
            {
                return _ZoomLessCommand
                    ?? (_ZoomLessCommand = new RelayCommand(
                                          () =>
                                          {
                                              // zoom less
                                              ExecuteZoomOperation(false);
                                          }));
            }
        }
        #endregion

        #region ZoomPlusComand
        private RelayCommand _ZoomPlusCommand;
        /// <summary>
        /// Gets the ZoomPlusComand.
        /// </summary>
        public RelayCommand ZoomPlusComand
        {
            get
            {
                return _ZoomPlusCommand
                    ?? (_ZoomPlusCommand = new RelayCommand(
                                          () =>
                                          {
                                              ExecuteZoomOperation(true);

                                          }));
            }
        }
        #endregion

        #region ZoomScaleX
        /// <summary>
        /// The <see cref="ZoomScaleX" /> property's name.
        /// </summary>
        public const string ZoomScaleXPropertyName = "ZoomScaleX";

        private double _zoomScaleX = 1;

        /// <summary>
        /// Sets and gets the ZoomScaleX property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public double ZoomScaleX
        {
            get
            {
                return _zoomScaleX;
            }

            set
            {
                if (_zoomScaleX == value)
                {
                    return;
                }

                _zoomScaleX = value;
                RaisePropertyChanged(ZoomScaleXPropertyName);
            }
        }
        #endregion

        #region ZoomScaleY
        /// <summary>
        /// The <see cref="ZoomScaleY" /> property's name.
        /// </summary>
        public const string ZoomScaleYPropertyName = "ZoomScaleY";

        private double _zoomScaleY = 1;

        /// <summary>
        /// Sets and gets the ZoomScaleY property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public double ZoomScaleY
        {
            get
            {
                return _zoomScaleY;
            }

            set
            {
                if (_zoomScaleY == value)
                {
                    return;
                }

                _zoomScaleY = value;
                RaisePropertyChanged(ZoomScaleYPropertyName);
            }
        }
        #endregion



        #region ScrollVerticalOffset
        /// <summary>
        /// The <see cref="ScrollVerticalOffset" /> property's name.
        /// </summary>
        public const string ScrollVerticalOffsetPropertyName = "ScrollVerticalOffset";

        private double _scrollVerticalOffset = 0;

        /// <summary>
        /// Sets and gets the ScrollVerticalOffset property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public double ScrollVerticalOffset
        {
            get
            {
                return _scrollVerticalOffset;
            }

            set
            {
                if (_scrollVerticalOffset == value)
                {
                    return;
                }

                _scrollVerticalOffset = value;
                RaisePropertyChanged(ScrollVerticalOffsetPropertyName);
            }
        }
        #endregion

        #region ScrollHorizontalOffset
        /// <summary>
        /// The <see cref="ScrollHorizontalOffset" /> property's name.
        /// </summary>
        public const string ScrollHorizontalOffsetPropertyName = "ScrollHorizontalOffset";

        private double _scrollHorizontalOffset = 0;

        /// <summary>
        /// Sets and gets the ScrollHorizontalOffset property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public double ScrollHorizontalOffset
        {
            get
            {
                return _scrollHorizontalOffset;
            }

            set
            {
                if (_scrollHorizontalOffset == value)
                {
                    return;
                }

                _scrollHorizontalOffset = value;
                RaisePropertyChanged(ScrollHorizontalOffsetPropertyName);
            }
        }
        #endregion

        #region VerticalScrollVisibility
        /// <summary>
        /// The <see cref="VerticalScrollVisibility" /> property's name.
        /// </summary>
        public const string VerticalScrollVisibilityPropertyName = "VerticalScrollVisibility";

        private ScrollBarVisibility _VerticalScrollVisibility = ScrollBarVisibility.Auto;

        /// <summary>
        /// Sets and gets the VerticalScrollVisibility property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ScrollBarVisibility VerticalScrollVisibility
        {
            get
            {
                return _VerticalScrollVisibility;
            }

            set
            {
                if (_VerticalScrollVisibility == value)
                {
                    return;
                }

                _VerticalScrollVisibility = value;
                RaisePropertyChanged(VerticalScrollVisibilityPropertyName);
            }
        }
        #endregion

        #region HorizontalScrollBarVisibility
        /// <summary>
        /// The <see cref="HorizontalScrollBarVisibility" /> property's name.
        /// </summary>
        public const string HorizontalScrollBarVisibilityPropertyName = "HorizontalScrollBarVisibility";

        private ScrollBarVisibility _HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;

        /// <summary>
        /// Sets and gets the HorizontalScrollBarVisibility property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ScrollBarVisibility HorizontalScrollBarVisibility
        {
            get
            {
                return _HorizontalScrollBarVisibility;
            }

            set
            {
                if (_HorizontalScrollBarVisibility == value)
                {
                    return;
                }

                _HorizontalScrollBarVisibility = value;
                RaisePropertyChanged(HorizontalScrollBarVisibilityPropertyName);
            }
        }
        #endregion

        public void ExecuteZoomOperation(bool zoomIn)
        {
            double zoomStep = (zoomIn) ? 0.2 : -0.2;
            bool zoomLimitReached = (!zoomIn && ZoomScaleX < 0.4)
                                ||
                                (zoomIn && ZoomScaleX > 2);


            if (!zoomLimitReached)
            {
                ZoomScaleX += zoomStep;
                ZoomScaleY += zoomStep;
                // TODO: Find a way to update the scroll View !!!!                
                CheckForScrollBarsVisibility();
            }
        }



        public void CheckForScrollBarsVisibility()
        {
            // ScrollViewer --> ScrViewer
            // Canvas       --> LayoutRoot            

            Size LayoutRootScaledSize = GetLayoutCanvasScaledSize();
            Debug.WriteLine("ActualWidthProperty = " + LayoutrootActualWidth.ToString());
            Debug.WriteLine("LayoutrootWidth = " + LayoutrootWidth.ToString() + " LayoutRootScaledSize = " + LayoutRootScaledSize.Width.ToString());
            bool isScrollViewXBiggerThanCanvas = (LayoutrootActualWidth > LayoutRootScaledSize.Width);
            bool isScrollViewYBiggerThanCanvas = (LayoutrootActualHeight > LayoutRootScaledSize.Height);

            // 1. First check if it's needed to show an scroll bar
            // HORIZONTAL AXIS
            HorizontalScrollBarVisibility = (isScrollViewXBiggerThanCanvas) ? ScrollBarVisibility.Hidden : ScrollBarVisibility.Visible;

            //  VERTICAL AXIS
            VerticalScrollVisibility = (isScrollViewYBiggerThanCanvas) ? ScrollBarVisibility.Hidden : ScrollBarVisibility.Visible;


            // 2. If we don't need to show an scroll bar, but we have an offset already scrolled
            // check if we have to scroll back in order to show the whole documnet
            // HORIZONTAL AXIS
            AdjustXOffsetBasedOnNewZoomParams(LayoutRootScaledSize);

            //  VERTICAL AXIS
            AdjustYOffsetBasedOnNewZoomParams(LayoutRootScaledSize);
        }
        Size GetLayoutCanvasScaledSize()
        {
            Size scaledSize = new Size();

            scaledSize.Width = LayoutrootWidth * ZoomScaleX;
            scaledSize.Height = LayoutrootHeight * ZoomScaleY;

            return scaledSize;
        }
        protected double UnscaleValueX(double value)
        {
            return (value / ZoomScaleX);
        }

        protected double UnscaleValueY(double value)
        {
            return (value / ZoomScaleY);
        }

        protected void AdjustYOffsetBasedOnNewZoomParams(Size LayoutRootScaledSize)
        {
            double ScaledVerticalOffset = ScrollVerticalOffset * ZoomScaleY;
            double freeVerticalSpaceInTheViewPort = (LayoutrootHeight - (LayoutRootScaledSize.Height - ScaledVerticalOffset));

            if (ScrollVerticalOffset > 0 &&
                freeVerticalSpaceInTheViewPort > 0
                )
            {
                double newOffsetPosition = (ScrollVerticalOffset) - UnscaleValueY(freeVerticalSpaceInTheViewPort);

                if (newOffsetPosition > 0)
                {

                    ScrollVerticalOffset = newOffsetPosition;
                }
                else
                {
                    ScrollVerticalOffset = 0;
                }
            }
        }
        protected void AdjustXOffsetBasedOnNewZoomParams(Size LayoutRootScaledSize)
        {
            double ScaledHorizontalOffset = ScrollHorizontalOffset * ZoomScaleX;
            double freeHorizontalSpaceInTheViewPort = (LayoutrootWidth - (LayoutRootScaledSize.Width - ScaledHorizontalOffset));

            if (ScrollHorizontalOffset > 0 &&
                freeHorizontalSpaceInTheViewPort > 0
                )
            {
                double newOffsetPosition = (ScrollHorizontalOffset) - UnscaleValueX(freeHorizontalSpaceInTheViewPort);

                if (newOffsetPosition > 0)
                {
                    ScrollHorizontalOffset = newOffsetPosition;
                }
                else
                {
                    ScrollHorizontalOffset = 0;
                }
            }
        }

        #endregion


        private RelayCommand<ShapeBase> _selectShape;

        /// <summary>
        /// Gets the SelectShapeCommand.
        /// </summary>
        public RelayCommand<ShapeBase> SelectShapeCommand
        {
            get
            {
                return _selectShape
                    ?? (_selectShape = new RelayCommand<ShapeBase>(
                                          p =>
                                          {
                                              if (p is MyNode)
                                              {

                                                  MyNode m = (MyNode)p;
                                                  SelectedDiagramItem = m;


                                              }

                                          }));
            }
        }



        private RelayCommand _selectNode;

        /// <summary>
        /// Gets the SelectNode.
        /// </summary>
        public RelayCommand SelectNode
        {
            get
            {
                return _selectNode
                    ?? (_selectNode = new RelayCommand(
                                          () =>
                                          {
                                              MessageBox.Show("11");
                                          }));
            }
        }

        private RelayCommand _NewFieldCommand;

        /// <summary>
        /// Gets the NewFieldCommand.
        /// </summary>
        public RelayCommand NewFieldCommand
        {
            get
            {
                return _NewFieldCommand
                    ?? (_NewFieldCommand = new RelayCommand(
                                          () =>
                                          {
                                              MyNode selEntity = (MyNode)SelectedDiagramItem;
                                              string solutionid = "FD140AAF-4DF4-11DD-BD17-0019B9312238"; // Default Solution
                                              if (SelectedSolution != null && SelectedSolution.SolutionId != Guid.Empty)
                                              {
                                                  solutionid = SelectedSolution.SolutionId.ToString("D");
                                              }
                                              string entityName = "";
                                              Uri serverUrl = null;
                                              if (selEntity != null)
                                              {
                                                  string entityID = "";
                                                  if (selEntity.Items.Count > 0)
                                                  {
                                                      entityID = selEntity.Items[0].EntityId.ToString("D");
                                                  }
                                                  else
                                                  {
                                                      entityID = selEntity.RemovedItems[0].EntityId.ToString("D");
                                                  }
                                                  serverUrl = new Uri(ClientScript.GetServerUrl() + @"/tools/systemcustomization/attributes/manageAttribute.aspx?appSolutionId=%7b" + solutionid + "%7d&entityId=%7b" +
                                                      entityID + "%7d");
                                                  entityName = selEntity.Name;
                                              }
                                              else if (SelectedAttribute != null)
                                              {
                                                  serverUrl = new Uri(ClientScript.GetServerUrl() + @"/tools/systemcustomization/attributes/manageAttribute.aspx?appSolutionId=%7b" + solutionid + "%7d&entityId=%7b" +
                                                      SelectedAttribute.EntityId.ToString("D") + "%7d");
                                                  entityName = SelectedAttribute.EntityLogicalName;
                                              }

                                              if (serverUrl != null)
                                              {
                                                  HtmlPage.Window.Invoke("OpenCRMwindow", serverUrl.ToString(), entityName);
                                              }

                                          }));
            }
        }

        public void RefreshNode(string entityName)
        {
            IsBusy = true;
            //Add to the select entity list back again.

            EntityClass bentity = new EntityClass("", entityName, false, Guid.Empty);
            bentity = _dataService.GetEntity(entityName, true, "", (item, error) =>
            {
                Entity = item;
                var exitstingentity = from p in MyEntityList
                                      where p is MyNode && ((MyNode)p).Name == entityName
                                      select p;

                if (exitstingentity != null && exitstingentity.Count() > 0)
                {
                    MyNode selEntity = (MyNode)(exitstingentity).First();

                    double x, y, w, h;
                    x = selEntity.Position.X;
                    y = selEntity.Position.Y;
                    w = selEntity.Width;
                    h = selEntity.Height;
                    _MyEntityList.Remove(selEntity);

                    AddEntity(x, y, w, h, null);
                }
                else
                {
                    bentity = item;
                    EntityList.Add(bentity);
                    //sort the list
                    EntityList = new ObservableCollection<EntityClass>(from ee in EntityList orderby ee.SchemaNameSet select ee);
                }
                IsBusy = false;
            });
        }


        private RelayCommand _deleteEntityCommand;

        /// <summary>
        /// Gets the DeleteEntityCommand.
        /// </summary>
        public RelayCommand DeleteEntityCommand
        {
            get
            {
                return _deleteEntityCommand
                    ?? (_deleteEntityCommand = new RelayCommand(
                                          () =>
                                          {
                                              if (System.Windows.Browser.HtmlPage.Window.Confirm("Are you sure you want to delete this entity?"))
                                              {
                                                  MyNode selEntity = (MyNode)SelectedDiagramItem;
                                                  string entitytodelete = selEntity.Name;

                                                  IsBusy = true;
                                                  _dataService.DeleteEntity(entitytodelete, (item, error) =>
                                                  {
                                                      RemoveEntity.Execute(null);
                                                      IsBusy = false;
                                                  });

                                              }
                                          },
                                          () =>
                                          {
                                              return (SelectedDiagramItem != null && SelectedDiagramItem is MyNode &&
                                                  ((MyNode)SelectedDiagramItem).IsCustomEntity == true &&
                                                  ((MyNode)SelectedDiagramItem).IsManagedEntity == false);
                                          }));
            }
        }

        private RelayCommand _deleteFieldCommand;

        /// <summary>
        /// Gets the DeleteFieldCommand.
        /// </summary>
        public RelayCommand DeleteFieldCommand
        {
            get
            {
                return _deleteFieldCommand
                    ?? (_deleteFieldCommand = new RelayCommand(
                                          () =>
                                          {
                                              if (System.Windows.Browser.HtmlPage.Window.Confirm("Are you sure you want to delete this field?"))
                                              {
                                                  MyNode selEntity = (MyNode)SelectedDiagramItem;
                                                  string entitytodelete = selEntity.Name;

                                                  IsBusy = true;
                                                  _dataService.DeleteField(SelectedAttribute.EntityLogicalName, SelectedAttribute.SchemaName, (item, error) =>
                                                  {
                                                      //RemoveEntity.Execute(null);
                                                      RefreshNode(SelectedAttribute.EntityLogicalName);
                                                      IsBusy = false;
                                                  });

                                              }

                                          },
                                          () =>
                                          {
                                              return (SelectedAttribute != null &&
                                                  SelectedAttribute.Metadata.IsCustomAttribute == true &&
                                                  SelectedAttribute.Metadata.IsManaged == false);
                                          }));
            }
        }
        private RelayCommand _editEnity;

        /// <summary>
        /// Gets the EditEntityCommand.
        /// </summary>
        public RelayCommand EditEntityCommand
        {
            get
            {
                return _editEnity
                    ?? (_editEnity = new RelayCommand(
                                          () =>
                                          {
                                              if (SelectedDiagramItem is MyNode)
                                              {
                                                  MyNode selEntity = (MyNode)SelectedDiagramItem;
                                                  string entityID = "";
                                                  if (selEntity.Items.Count > 0)
                                                  {
                                                      entityID = selEntity.Items[0].EntityId.ToString("D");
                                                  }
                                                  else
                                                  {
                                                      entityID = selEntity.RemovedItems[0].EntityId.ToString("D");
                                                  }
                                                  Uri serverUrl = new Uri(ClientScript.GetServerUrl() + @"/tools/systemcustomization/entities/manageentity.aspx?appSolutionId=%7bFD140AAF-4DF4-11DD-BD17-0019B9312238%7d&id=%7b" +
                                                      entityID + "%7d");

                                                  HtmlPage.Window.Invoke("OpenCRMwindow", serverUrl.ToString(), selEntity.Name);
                                              }


                                          }));
            }
        }

        private RelayCommand _addNewSolution;

        /// <summary>
        /// Gets the AddNewEntity.
        /// </summary>
        public RelayCommand AddNewSolution
        {
            get
            {
                return _addNewSolution
                    ?? (_addNewSolution = new RelayCommand(
                                          () =>
                                          {
                                              Uri serverUrl = new Uri(ClientScript.GetServerUrl() + @"/tools/solution/edit.aspx");
                                              HtmlPage.Window.Invoke("OpenCRMwindow", serverUrl.ToString(), "");
                                          }));
            }
        }


        private RelayCommand _addNewEntity;

        /// <summary>
        /// Gets the AddNewEntity.
        /// </summary>
        public RelayCommand AddNewEntity
        {
            get
            {
                return _addNewEntity
                    ?? (_addNewEntity = new RelayCommand(
                                          () =>
                                          {
                                              string solutionid = "FD140AAF-4DF4-11DD-BD17-0019B9312238"; // Default Solution
                                              if (SelectedSolution != null && SelectedSolution.SolutionId != Guid.Empty)
                                              {
                                                  solutionid = SelectedSolution.SolutionId.ToString("D");
                                              }
                                              Uri serverUrl = new Uri(ClientScript.GetServerUrl() + @"/tools/systemcustomization/entities/manageentity.aspx?appSolutionId=%7b" + solutionid + "%7d");
                                              HtmlPage.Window.Invoke("OpenCRMwindow", serverUrl.ToString(), "");
                                          }));
            }
        }


        private RelayCommand _openAttibuteDetails;

        public long LastTicks = 0;
        /// <summary>
        /// Gets the OpenAttributeDetails.
        /// </summary>
        public RelayCommand OpenAttributeDetails
        {
            get
            {
                return _openAttibuteDetails
                    ?? (_openAttibuteDetails = new RelayCommand(
                                          () =>
                                          {
                                              if ((DateTime.Now.Ticks - LastTicks) < 3000000) // to simulate double click 
                                              {

                                                  EditAttribute();
                                              }
                                              LastTicks = DateTime.Now.Ticks;
                                          }));
            }
        }

        private RelayCommand _editAttributeCommand;

        /// <summary>
        /// Gets the EditAttributeCommand.
        /// </summary>
        public RelayCommand EditAttributeCommand
        {
            get
            {
                return _editAttributeCommand
                    ?? (_editAttributeCommand = new RelayCommand(
                                          () =>
                                          {
                                              EditAttribute();
                                          }));
            }
        }
        private void EditAttribute()
        {
            //((ERDDiagram.CrmSdk.MetadataBase)(p.Metadata)).MetadataId
            string solutionid = "FD140AAF-4DF4-11DD-BD17-0019B9312238"; // Default Solution


            Uri serverUrl = new Uri(ClientScript.GetServerUrl() + @"/tools/systemcustomization/attributes/manageAttribute.aspx?appSolutionId=%7b" + solutionid + "%7d&attributeId=%7b" +
                ((ERDDiagram.CrmSdk.MetadataBase)(SelectedAttribute.Metadata)).MetadataId.Value.ToString("D") +
                "%7d&entityId=%7b" +
                SelectedAttribute.EntityId.ToString("D") + "%7d");

            HtmlPage.Window.Invoke("OpenCRMwindow", serverUrl.ToString(), SelectedAttribute.EntityLogicalName);

        }

        private RelayCommand _editRelationshipCommand;

        /// <summary>
        /// Gets the EditAttributeCommand.
        /// </summary>
        public RelayCommand EditRelationshipCommand
        {
            get
            {
                return _editRelationshipCommand
                    ?? (_editRelationshipCommand = new RelayCommand(
                                          () =>
                                          {
                                              EditRelationship();
                                          }));
            }
        }
        private void EditRelationship()
        {
            //((ERDDiagram.CrmSdk.MetadataBase)(p.Metadata)).MetadataId
            string solutionid = "FD140AAF-4DF4-11DD-BD17-0019B9312238"; // Default Solution


            if (SelectedDiagramItem != null && SelectedDiagramItem is MyConnection)
            {

                Uri serverUrl = new Uri(ClientScript.GetServerUrl() + @"/tools/systemcustomization/relationships/manageRelationship.aspx?appSolutionId=%7b" + solutionid + "%7d"
                    + "&entityId=%7b" + ((MyConnection)(SelectedDiagramItem)).EntityId.ToString("D") + "%7d" 
                    + "&entityRelationshipId=%7b" + ((MyConnection)(SelectedDiagramItem)).MetadataId.ToString("D") + "%7d");
                    

                HtmlPage.Window.Invoke("OpenCRMwindow", serverUrl.ToString(), "");
            }
        }

        private RelayCommand _AddLookUpentity_Click;

        /// <summary>
        /// Gets the AddLookUpentity_Click.
        /// </summary>
        public RelayCommand AddLookUpentity_Click
        {
            get
            {
                return _AddLookUpentity_Click
                    ?? (_AddLookUpentity_Click = new RelayCommand(
                                          () =>
                                          {
                                              if (SelectedAttribute.Metadata is LookupAttributeMetadata)
                                              {
                                                  LookupAttributeMetadata attr = (LookupAttributeMetadata)SelectedAttribute.Metadata;
                                                  if (attr.Targets.Count > 0)
                                                  {
                                                      _selectedEntity = new EntityClass("", attr.Targets[0], false, SelectedAttribute.EntityId);

                                                      var exitstingentity = from p in MyEntityList
                                                                            where p is MyNode && ((MyNode)p).Name == attr.Targets[0]
                                                                            select p;

                                                      if (exitstingentity != null && exitstingentity.Count() > 0)
                                                      {
                                                          return;
                                                      }
                                                      IsBusy = true;
                                                      Entity = _dataService.GetEntity(attr.Targets[0], true, "", (item, error) =>
                                                      {
                                                          Entity = item;

                                                          MyNode selEntity = (MyNode)(from p in MyEntityList
                                                                                      where p is MyNode && ((MyNode)p).Name == SelectedAttribute.EntityLogicalName
                                                                                      select p).First();

                                                          double posX = selEntity.Position.X + selEntity.Width + 100;
                                                          // calculate Height based on attributes count
                                                          
                                                          AddEntity(posX, 100, 200, 200, null);
                                                          IsBusy = false;
                                                      });
                                                  }
                                              }
                                          }));
            }
        }

        /// <summary>
        /// The <see cref="SelectedAttribute" /> property's name.
        /// </summary>
        public const string SelectedAttributePropertyName = "SelectedAttribute";

        private AttributeClass _SelectedAttribute = null;


        /// <summary>
        /// Sets and gets the SelectedAttribute property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public AttributeClass SelectedAttribute
        {
            get
            {
                return _SelectedAttribute;
            }

            set
            {
                if (_SelectedAttribute == value)
                {
                    return;
                }

                _SelectedAttribute = value;
                RaisePropertyChanged(SelectedAttributePropertyName);
                DeleteFieldCommand.RaiseCanExecuteChanged();
            }
        }


        /// <summary>
        /// The <see cref="SelectedEntity" /> property's name.
        /// </summary>
        public const string SelectedSolutionPropertyName = "SelectedSolution";

        private SolutionClass _selectedSolution = null;

        /// <summary>
        /// Sets and gets the SelectedEntity property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public SolutionClass SelectedSolution
        {
            get
            {
                return _selectedSolution;
            }

            set
            {
                if (_selectedSolution == value)
                {
                    return;
                }

                _selectedSolution = value;
                if (_selectedSolution != null)
                {
                    if (_selectedSolution.SolutionId == Guid.Empty)
                    {

                        AddNewSolution.Execute(null);
                        SelectedSolution = null;
                    }
                }
                RaisePropertyChanged(SelectedSolutionPropertyName);

                GenerateDiagramFromSolution.RaiseCanExecuteChanged();
            }
        }



        /// <summary>
        /// The <see cref="SelectedEntity" /> property's name.
        /// </summary>
        public const string SelectedEntityPropertyName = "SelectedEntity";

        private EntityClass _selectedEntity = null;

        /// <summary>
        /// Sets and gets the SelectedEntity property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public EntityClass SelectedEntity
        {
            get
            {
                return _selectedEntity;
            }

            set
            {
                if (_selectedEntity == value)
                {
                    return;
                }

                _selectedEntity = value;
                if (_selectedEntity != null)
                {
                    if (_selectedEntity.LogicalName == "new")
                    {
                        AddNewEntity.Execute(null);
                        SelectedEntity = null;
                    }
                    else
                    {
                        ProcessSelectedEntityMetaData(_selectedEntity.LogicalName);
                    }

                }
                RaisePropertyChanged(SelectedEntityPropertyName);

                AddEntity_Click.RaiseCanExecuteChanged();
            }
        }


        /// <summary>
        /// The <see cref="IsDirty" /> property's name.
        /// </summary>
        public const string IsDirtyPropertyName = "IsDirty";

        private bool _isDirty = false;

        /// <summary>
        /// Sets and gets the IsDirty property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsDirty
        {
            get
            {
                return _isDirty;
            }

            set
            {
                if (_isDirty == value)
                {
                    return;
                }

                _isDirty = value;
                RaisePropertyChanged(IsDirtyPropertyName);
            }
        }
        /// <summary>
        /// The <see cref="IsBusy" /> property's name.
        /// </summary>
        public const string IsBusyPropertyName = "IsBusy";

        private bool _IsBusy = false;

        /// <summary>
        /// Sets and gets the IsBusy property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsBusy
        {
            get
            {
                return _IsBusy;
            }

            set
            {
                if (_IsBusy == value)
                {
                    return;
                }

                _IsBusy = value;
                RaisePropertyChanged(IsBusyPropertyName);
            }
        }
        private void ProcessSelectedEntityMetaData(String EntityLogicalName)
        {
            IsBusy = true;
            Entity = _dataService.GetEntity(EntityLogicalName, true, "", (item, error) =>
            {
                Entity = item;
                IsBusy = false;
            });

        }


        /// <summary>
        /// The <see cref="Entity" /> property's name.
        /// </summary>
        public const string EntityPropertyName = "Entity";

        private EntityClass _entity = null;

        /// <summary>
        /// Sets and gets the Entity property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public EntityClass Entity
        {
            get
            {
                return _entity;
            }

            set
            {
                if (_entity == value)
                {
                    return;
                }

                _entity = value;
                RaisePropertyChanged(EntityPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="SelectedEntityType" /> property's name.
        /// </summary>
        public const string SelectedEntityTypePropertyName = "SelectedEntityType";

        private EntityTypeClass _SelectedEntityType = new EntityTypeClass() { TypeName = "All" };

        /// <summary>
        /// Sets and gets the SelectedEntityType property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public EntityTypeClass SelectedEntityType
        {
            get
            {
                return _SelectedEntityType;
            }

            set
            {
                if (_SelectedEntityType == value)
                {
                    return;
                }

                _SelectedEntityType = value;
                if (_SelectedEntityType != null)
                {
                    if (_SelectedEntityType.TypeName == "All" && EntitiesOrig != null)
                    {
                        EntityList = new ObservableCollection<EntityClass>(from ee in EntitiesOrig select ee);
                    }
                    else if (_SelectedEntityType.TypeName == "System")
                    {
                        EntityList = new ObservableCollection<EntityClass>(from ee in EntitiesOrig where ee.IsCustomEntity != true select ee);
                    }
                    else if (_SelectedEntityType.TypeName == "Custom")
                    {
                        EntityList = new ObservableCollection<EntityClass>(from ee in EntitiesOrig where ee.IsCustomEntity == true select ee);
                    }
                    RaisePropertyChanged(SelectedEntityTypePropertyName);
                }
            }
        }

        #region EntityTypeList
        /// <summary>
        /// The <see cref="EntityTypeList" /> property's name.
        /// </summary>
        public const string EntityTypeListPropertyName = "EntityTypeList";

        private ObservableCollection<EntityTypeClass> _EntityTypeList = new ObservableCollection<EntityTypeClass>() { new EntityTypeClass() { TypeName = "All" }, 
                                                                                                                      new EntityTypeClass() { TypeName = "Custom" },
                                                                                                                      new EntityTypeClass() { TypeName = "System" } };


        /// <summary>
        /// Sets and gets the EntityTypeList property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ObservableCollection<EntityTypeClass> EntityTypeList
        {
            get
            {
                return _EntityTypeList;
            }

            set
            {
                if (_EntityTypeList == value)
                {
                    return;
                }

                _EntityTypeList = value;
                RaisePropertyChanged(EntityTypeListPropertyName);
            }
        }
        #endregion

        /// <summary>
        /// The <see cref="EntitiesOrig" /> property's name.
        /// </summary>
        public const string EntitiesOrigPropertyName = "EntitiesOrig";

        private ObservableCollection<EntityClass> _entitiesOrig = null;

        /// <summary>
        /// Sets and gets the EntitiesOrig property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ObservableCollection<EntityClass> EntitiesOrig
        {
            get
            {
                return _entitiesOrig;
            }

            set
            {
                if (_entitiesOrig == value)
                {
                    return;
                }

                _entitiesOrig = value;
                RaisePropertyChanged(EntitiesOrigPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="SolutionList" /> property's name.
        /// </summary>
        public const string SolutionListPropertyName = "SolutionList";
        private ObservableCollection<SolutionClass> _SolutionList;

        public ObservableCollection<SolutionClass> SolutionList
        {
            get { return _SolutionList; }
            private set
            {
                _SolutionList = value;
                RaisePropertyChanged(SolutionListPropertyName);

            }
        }
        /// <summary>
        /// The <see cref="MyEntityList" /> property's name.
        /// </summary>
        public const string EntityListPropertyName = "EntityList";
        private ObservableCollection<EntityClass> _entities;

        public ObservableCollection<EntityClass> EntityList
        {
            get { return _entities; }
            private set
            {
                _entities = value;
                RaisePropertyChanged(EntityListPropertyName);

            }
        }

        public const string MyEntityListPropertyName = "MyEntityList";
        private ObservableCollection<ShapeBase> _MyEntityList = null;
        /// <summary>
        /// Sets and gets the MyEntityList property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ObservableCollection<ShapeBase> MyEntityList
        {
            get
            {
                return _MyEntityList;
            }

            set
            {
                if (_MyEntityList == value)
                {
                    return;
                }

                _MyEntityList = value;
                RaisePropertyChanged(MyEntityListPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="SelectedDiagramItem" /> property's name.
        /// </summary>
        public const string SelectedDiagramItemPropertyName = "SelectedDiagramItem";

        private ShapeBase _SelectedDiagramItem = null;

        /// <summary>
        /// Sets and gets the SelectedDiagramItem property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ShapeBase SelectedDiagramItem
        {
            get
            {
                return _SelectedDiagramItem;
            }

            set
            {
                if (_SelectedDiagramItem == value)
                {
                    return;
                }

                _SelectedDiagramItem = value;
                RaisePropertyChanged(SelectedDiagramItemPropertyName);
                DeleteEntityCommand.RaiseCanExecuteChanged();
            }
        }
        /// <summary>
        /// The <see cref="WelcomeTitle" /> property's name.
        /// </summary>
        public const string WelcomeTitlePropertyName = "WelcomeTitle";

        private string _welcomeTitle = string.Empty;

        /// <summary>
        /// Gets the WelcomeTitle property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string WelcomeTitle
        {
            get
            {
                return _welcomeTitle;
            }

            set
            {
                if (_welcomeTitle == value)
                {
                    return;
                }

                _welcomeTitle = value;
                RaisePropertyChanged(WelcomeTitlePropertyName);
            }
        }

        private RelayCommand _showAbout;

        /// <summary>
        /// Gets the ShowAbout.
        /// </summary>
        public RelayCommand ShowAbout
        {
            get
            {
                return _showAbout
                    ?? (_showAbout = new RelayCommand(
                                          () =>
                                          {
                                              About aboutWin = new About();
                                              aboutWin.Show();

                                          }));
            }
        }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel(IDataService dataService)
        {

            if (App.Current.Host.InitParams.ContainsKey("id"))
            {
                DiagramID = App.Current.Host.InitParams["id"].ToString();
            }
            else
            {
                DiagramID = "{C933E430-88AC-E111-A9F1-00155DFE7909}";
            }
            _dataService = dataService;
            IsBusy = true;
            _MyEntityList = new ObservableCollection<ShapeBase>();
            _entity = new EntityClass();
            ERDDiagramFile myRecord = new ERDDiagramFile();

            _SolutionList = _dataService.GetSolutions((itemSol, error) =>
            {

            });
            _entities = _dataService.GetEntities((item, error) =>
            {
                EntitiesOrig = new ObservableCollection<EntityClass>(from ee in _entities select ee);

                if (!String.IsNullOrEmpty(DiagramID))
                {
                    _dataService.GetDiagramData(DiagramID, (itemNode, errorNode) =>
                    {
                        if (itemNode != null && itemNode.Title.Trim().Length > 0)
                        {
                            XmlSerializer serializer = new XmlSerializer(typeof(ERDDiagramFile));
                            XmlReaderSettings settings = new XmlReaderSettings();
                            // No settings need modifying here

                            using (StringReader textReader = new StringReader(itemNode.Title))
                            {
                                using (XmlReader xmlReader = XmlReader.Create(textReader, settings))
                                {
                                    myRecord = (ERDDiagramFile)serializer.Deserialize(xmlReader);
                                }
                            }

                            LayoutrootHeight = myRecord.Height;
                            LayoutrootWidth = myRecord.Width;
                            DrawNodes(myRecord);
                        }
                    });
                }
                IsBusy = false;
            });


            _dataService.GetData(
                (item, error) =>
                {
                    if (error != null)
                    {
                        // Report error here
                        return;
                    }

                    WelcomeTitle = item.Title;
                });

            SelectedEntityType = EntityTypeList.First();

        }

        private void DrawNodes(ERDDiagramFile diagramFile)
        {
            if (diagramFile.Nodes.Count > 0)
            {
                _entity = _dataService.GetEntity(diagramFile.Nodes[0].EntityName, true, "", (item3, error2) =>
                {
                    if (item3.Attributes.Count > 0)
                    {
                        foreach (string attributename in diagramFile.Nodes[0].RemovedAttributes)
                        {
                            AttributeClass attr = (AttributeClass)(from ee in item3.Attributes
                                                                   where ee.SchemaName == attributename
                                                                   select ee).First();
                            item3.Attributes.Remove(attr);
                            item3.RemovedAttributes.Add(attr);

                        }

                        _selectedEntity = item3;
                        Entity = item3;
                        AddEntity(diagramFile.Nodes[0].Left, diagramFile.Nodes[0].Top, diagramFile.Nodes[0].Height, diagramFile.Nodes[0].Width, diagramFile);
                        diagramFile.Nodes.RemoveAt(0);
                        DrawNodes(diagramFile);
                    }
                    IsBusy = false;
                    SelectedEntity = null;
                    CheckForScrollBarsVisibility();
                    ScrollHorizontalOffset = 10;

                });

            }
        }

        private void DrawNodesFromSolution(ObservableCollection<EntityClass> entities, double staringPoint)
        {

            if (entities.Count > 0)
            {
                _entity = _dataService.GetEntityById(entities[0].EntityId, (item3, error2) =>
                {
                    if (item3.Attributes.Count > 0)
                    {
                        // check if the entity is alreayd in the diagram first
                        bool found = false;
                        foreach (ShapeBase item in MyEntityList)
                        {
                            if (item is MyNode && String.Compare(item3.LogicalName, item.Name, StringComparison.InvariantCultureIgnoreCase) == 0)
                            {
                                found = true;
                            }
                        }
                        _selectedEntity = item3;
                        Entity = item3;
                        if (!found)
                        {
                            AddEntity(staringPoint, 20, 200, 200, null);
                            LayoutrootWidth = LayoutrootWidth + 300;

                        }
                        entities.RemoveAt(0);
                        if (entities.Count == 0)
                        {
                            IsBusy = false;
                            SelectedEntity = null;
                            CheckForScrollBarsVisibility();
                            ScrollHorizontalOffset = 10;
                        }
                        else
                        {
                            DrawNodesFromSolution(entities, staringPoint + 300);
                        }
                    }

                });

            }

        }


        private RelayCommand _saveDiagram;

        /// <summary>
        /// Gets the SaveDiagram.
        /// </summary>
        public RelayCommand SaveDiagram
        {
            get
            {
                return _saveDiagram
                    ?? (_saveDiagram = new RelayCommand(
                                          () =>
                                          {

                                              string data = "";
                                              IsBusy = true;
                                              ERDDiagramFile myDiagramFile = new ERDDiagramFile();
                                              myDiagramFile.Width = LayoutrootWidth;
                                              myDiagramFile.Height = LayoutrootHeight;

                                              myDiagramFile.Nodes = new ObservableCollection<SimpleNodes>();
                                              myDiagramFile.Connections = new ObservableCollection<SimpleConnection>();
                                              foreach (ShapeBase item in MyEntityList)
                                              {
                                                  if (item is MyNode)
                                                  {
                                                      MyNode mynode = (MyNode)item;
                                                      SimpleNodes tnode = new SimpleNodes();
                                                      tnode.EntityName = mynode.Name;
                                                      tnode.Height = mynode.Height;
                                                      tnode.Width = mynode.Width;
                                                      tnode.Left = mynode.Position.X;
                                                      tnode.Top = mynode.Position.Y;
                                                      tnode.RemovedAttributes = new List<string>();
                                                      foreach (AttributeClass attritem in mynode.RemovedItems)
                                                      {
                                                          tnode.RemovedAttributes.Add(attritem.SchemaName);
                                                      }
                                                      myDiagramFile.Nodes.Add(tnode);
                                                  }
                                                  else if (item is MyConnection)
                                                  {
                                                      MyConnection myconn = (MyConnection)item;
                                                      SimpleConnection tConn = new SimpleConnection();
                                                      tConn.MetadataId = myconn.MetadataId;
                                                      tConn.IndexSource = myconn.Source.ConnectorIndex;
                                                      tConn.indexSink = myconn.Sink.ConnectorIndex;
                                                      myDiagramFile.Connections.Add(tConn);
                                                  }

                                              }

                                              XmlWriterSettings settings = new XmlWriterSettings();
                                              settings.Encoding = new UnicodeEncoding(false, false); // no BOM in a .NET string
                                              settings.Indent = false;
                                              settings.OmitXmlDeclaration = false;
                                              XmlSerializer serializer = new XmlSerializer(typeof(ERDDiagramFile));
                                              using (StringWriter textWriter = new StringWriter())
                                              {
                                                  using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings))
                                                  {
                                                      serializer.Serialize(xmlWriter, myDiagramFile);
                                                  }
                                                  data = textWriter.ToString();
                                              }

                                              ScriptObject parent = (ScriptObject)HtmlPage.Window.GetProperty("parent");
                                              ScriptObject xrm = (ScriptObject)parent.GetProperty("Xrm");

                                              _dataService.SaveDiagramData(DiagramID, data, (item, error) =>
                                              {
                                                  IsBusy = false;
                                                  if (this.CloseForm && this.IsDirty)
                                                  {
                                                      ScriptObject page = (ScriptObject)xrm.GetProperty("Page");
                                                      ScriptObject data2 = (ScriptObject)page.GetProperty("data");
                                                      ScriptObject entity2 = (ScriptObject)data2.GetProperty("entity");
                                                      this.IsDirty = false;
                                                      entity2.Invoke("save", "saveandclose");

                                                  }
                                                  this.IsDirty = false;

                                              });

                                          }));
            }
        }

        private RelayCommand _removeField;

        /// <summary>
        /// Gets the RemoveField.
        /// </summary>
        public RelayCommand RemoveField
        {
            get
            {
                return _removeField
                    ?? (_removeField = new RelayCommand(
                                          () =>
                                          {
                                              var exitstingentity = from p in MyEntityList
                                                                    where p is MyNode && ((MyNode)p).Name == SelectedAttribute.EntityLogicalName
                                                                    select p;

                                              if (exitstingentity != null && exitstingentity.Count() > 0)
                                              {
                                                  MyNode selEntity = (MyNode)(exitstingentity).First();
                                                  selEntity.Items.Remove(SelectedAttribute);
                                                  selEntity.RemovedItems.Add(SelectedAttribute);

                                                  SelectedAttribute = null;
                                              }


                                          }));
            }
        }

        private RelayCommand _removeRelationship;

        /// <summary>
        /// Gets the RemoveField.
        /// </summary>
        public RelayCommand RemoveRelationship
        {
            get
            {
                return _removeRelationship
                    ?? (_removeRelationship = new RelayCommand(
                                          () =>
                                          {

                                              if (SelectedDiagramItem != null && SelectedDiagramItem is MyConnection)
                                              {
                                                  MyEntityList.Remove(SelectedDiagramItem);

                                                  SelectedDiagramItem = null;
                                              }


                                          }));
            }
        }

        private RelayCommand _removeSystemFields;

        /// <summary>
        /// Gets the RemoveSystemFields.
        /// </summary>
        public RelayCommand RemoveSystemFields
        {
            get
            {
                return _removeSystemFields
                    ?? (_removeSystemFields = new RelayCommand(
                                          () =>
                                          {

                                              MyNode mNode = (MyNode)SelectedDiagramItem;
                                              ObservableCollection<AttributeClass> tem_emt = new ObservableCollection<AttributeClass>();
                                              foreach (AttributeClass item in mNode.Items)
                                              {
                                                  tem_emt.Add(item);
                                              }

                                              foreach (AttributeClass item in tem_emt)
                                              {
                                                  if (!item.Metadata.IsCustomAttribute.Value && !item.Metadata.IsPrimaryId.Value)
                                                  {
                                                      mNode.Items.Remove(item);
                                                      mNode.RemovedItems.Add(item);
                                                  }
                                              }
                                              if (mNode.Items.Count > 0)
                                              {
                                                  SelectedAttribute = mNode.Items.First();
                                              }
                                              else
                                              {
                                                  SelectedAttribute = null;
                                              }
                                          }));
            }
        }

        private RelayCommand _RemoveEntity;

        /// <summary>
        /// Gets the RemoveEntity.
        /// </summary>
        public RelayCommand RemoveEntity
        {
            get
            {
                return _RemoveEntity
                    ?? (_RemoveEntity = new RelayCommand(
                                          () =>
                                          {
                                              if (SelectedDiagramItem is MyNode)
                                              {
                                                  // remove links
                                                  ObservableCollection<ShapeBase> tem_emt = new ObservableCollection<ShapeBase>();
                                                  foreach (ShapeBase itemEntity in _MyEntityList)
                                                  {
                                                      tem_emt.Add(itemEntity);
                                                  }

                                                  foreach (ShapeBase itemRel in tem_emt)
                                                  {

                                                      if (itemRel is MyConnection && ((MyConnection)itemRel).Source.ConnectedNode == (MyNode)SelectedDiagramItem)
                                                      {
                                                          _MyEntityList.Remove(itemRel);
                                                      }
                                                      if (itemRel is MyConnection && ((MyConnection)itemRel).Sink.ConnectedNode == (MyNode)SelectedDiagramItem)
                                                      {
                                                          _MyEntityList.Remove(itemRel);
                                                      }
                                                  }
                                                  IsBusy = true;
                                                  //Add to the select entity list back again.
                                                  MyNode mNode = (MyNode)SelectedDiagramItem;
                                                  EntityClass bentity = new EntityClass("", mNode.Name, false, Guid.Empty);
                                                  bentity = _dataService.GetEntity(mNode.Name, true, "", (item, error) =>
                                                  {
                                                      bentity = item;


                                                      EntitiesOrig.Add(bentity);
                                                      //sort the list
                                                      EntityTypeClass old = SelectedEntityType;
                                                      SelectedEntityType = null;
                                                      EntitiesOrig = new ObservableCollection<EntityClass>(from ee in EntitiesOrig orderby ee.SchemaNameSet select ee);
                                                      SelectedEntityType = old;
                                                      IsBusy = false;
                                                  });



                                                  _MyEntityList.Remove(SelectedDiagramItem);
                                                  IsDirty = true;
                                              }

                                          }));
            }
        }

        private RelayCommand _AddEntity_Click;

        /// <summary>
        /// Gets the AddEntity_Click.
        /// </summary>
        public RelayCommand AddEntity_Click
        {
            get
            {
                return _AddEntity_Click
                    ?? (_AddEntity_Click = new RelayCommand(
                                          () =>
                                          {
                                              var exitstingentity = from p in MyEntityList
                                                                    where p is MyNode && ((MyNode)p).Name == _entity.LogicalName
                                                                    select p;

                                              if (!(exitstingentity != null && exitstingentity.Count() > 0))
                                              {

                                                  AddEntity(100, 100, 200, 200, null);
                                              }

                                          },
                                          () => { return (SelectedEntity != null); }));
            }
        }

        private void AddEntity(double x, double y, double height, double width, ERDDiagramFile diagramFile)
        {
            if (_entity != null)
            {

                IsDirty = true;
                MyNode node = new MyNode();
                node.Text = _entity.SchemaNameSet;
                node.Name = _entity.LogicalName;
                node.IsCustomEntity = _entity.IsCustomEntity;
                node.IsManagedEntity = _entity.IsManagedEntity;
                if (node.IsCustomEntity)
                {
                    node.CustomBackground = new SolidColorBrush(Color.FromArgb(0xFF, 0x37, 0x37, 0xC0));

                }
                else
                {
                    node.CustomBackground = new SolidColorBrush(Color.FromArgb(0xFF, 0x5C, 0x0C, 0x0C));
                }
                node.Position = new System.Windows.Point(x, y);
                node.Height = height;
                node.Width = width;
                foreach (AttributeClass attr in _entity.RemovedAttributes)
                {
                    node.RemovedItems.Add(attr);
                }
                foreach (AttributeClass attr in _entity.Attributes)
                {
                    node.Items.Add(attr);
                }
                MyEntityList.Add(node);
                SelectedDiagramItem = node;
                //// add links
                ObservableCollection<ShapeBase> tem_emt = new ObservableCollection<ShapeBase>();
                foreach (ShapeBase itemEntity in _MyEntityList)
                {
                    tem_emt.Add(itemEntity);
                }

                foreach (RelationshipClass itemRel in _entity.Relationships_OneToMany)
                {

                    foreach (ShapeBase itemEntity in tem_emt)
                    {

                        if (itemEntity is MyNode && itemEntity.Name == itemRel.EntityLogicalName)
                        {
                            AddRelationship(itemEntity, node, itemRel.EntityRelationshipId, _entity.EntityId, "1", diagramFile, itemRel.SchemaName);

                        }
                    }
                }
                foreach (RelationshipClass itemRel in _entity.Relationships_ManyToOne)
                {
                    foreach (ShapeBase itemEntity in tem_emt)
                    {
                        if (itemEntity is MyNode && itemEntity.Name == itemRel.EntityLogicalName && itemRel.EntityLogicalName != _selectedEntity.LogicalName)
                        {
                            AddRelationship(node, itemEntity, itemRel.EntityRelationshipId, _entity.EntityId, "1", diagramFile, itemRel.SchemaName);
                        }
                    }
                }
                foreach (RelationshipClass itemRel in _entity.Relationships_ManyToMany)
                {
                    var entity1 = from e in tem_emt
                                  where e.Name == itemRel.EntityLogicalName
                                  select e;
                    var entity2 = from e in tem_emt
                                  where e.Name == itemRel.PrimaryEntity
                                  select e;

                    if (entity1.Count() > 0 && entity2.Count() > 0)
                    {
                        AddRelationship((ShapeBase)entity1.First(), (ShapeBase)entity2.First(), itemRel.EntityRelationshipId, _entity.EntityId, "0..N", diagramFile, itemRel.SchemaName);
                    }
                }
                foreach (AttributeClass attr in _entity.Attributes)
                {
                    if (attr.AttributeType.Value == AttributeTypeCode.Lookup)
                    {
                        CrmSdk.LookupAttributeMetadata lookup = (CrmSdk.LookupAttributeMetadata)(attr.Metadata);
                        string linktoentity = "";
                        if (lookup.Targets.Count > 0)
                        {
                            linktoentity = lookup.Targets[0];
                        }
                        foreach (ShapeBase itemEntity in tem_emt)
                        {
                            if (itemEntity is MyNode && itemEntity.Name == linktoentity && linktoentity != _selectedEntity.LogicalName)
                            {
                                AddRelationship(node, itemEntity, Guid.Empty, Guid.Empty, "1", diagramFile, lookup.SchemaName);
                            }
                        }
                    }
                }

                var exitstingentity = from p in EntityList
                                      where p.SchemaNameSet == _entity.SchemaNameSet
                                      select p;

                if (exitstingentity != null && exitstingentity.Count() > 0)
                {

                    EntityClass entityToremove = (EntityClass)(exitstingentity).First();
                    EntityList.Remove(entityToremove);
                    if (EntitiesOrig != null)
                    {
                        EntitiesOrig.Remove((from ee in EntitiesOrig where ee.LogicalName == entityToremove.LogicalName select ee).First());
                    }
                }

                AddEntity_Click.RaiseCanExecuteChanged();

            }
        }

        private int GetSourceIndex(MyNode node)
        {
            int indexSource = 0;            
            foreach (IConnector conn in node.Connectors)
            {
                if (conn.Connections.Count == 0)
                {
                    break;
                }
                indexSource = indexSource + 1;
            }
            return indexSource;
        }

        
        private int GetAttributeIndex2(MyNode node, string PrimaryEntity)
        {
            int indexSource = 0;
            bool found = false;
            foreach (AttributeClass attritrm in node.Items)
            {
                if (String.Compare(attritrm.SchemaName, PrimaryEntity, StringComparison.InvariantCultureIgnoreCase) == 0)
                {
                    found = true;
                    break;
                }
                indexSource = indexSource + 1;
            }
            if (!found)
            {
                indexSource = 0;
                foreach (AttributeClass attritrm in node.Items)
                {
                    if (String.Compare(attritrm.TargetRecordType + "id", PrimaryEntity, StringComparison.InvariantCultureIgnoreCase) == 0)
                    {
                        found = true;
                        break;
                    }
                    indexSource = indexSource + 1;
                }
            }
            return indexSource;
        }

        private void AddRelationship(ShapeBase node, ShapeBase itemEntity, Guid metadataID, Guid entityId, string SinkText, ERDDiagramFile diagramFile, string relationName)
        {
            int indexSource = 0;
            int indexSink = 0;
            // check if the relationship comes from the diagram file
            if (diagramFile != null && diagramFile.Connections != null)
            {
                var connfile = from e in diagramFile.Connections 
                               where e.MetadataId == metadataID
                               select e;
                if (connfile != null && connfile.Count() > 0)
                {
                    indexSource = connfile.First().IndexSource;
                    indexSink = connfile.First().indexSink;
                }
                else
                {
                    // do not display this relationship
                    return;
                }
            }
            else
            {
                indexSource = GetSourceIndex((MyNode)node);
                indexSink = GetSourceIndex((MyNode)itemEntity);
                if (node.Name == itemEntity.Name)
                {
                    indexSink = indexSink + 1;
                }
            }

            MyConnection conn = new MyConnection();
            conn.MetadataId = metadataID;
            conn.EntityId = entityId;
            conn.TooltipText = relationName;
            conn.PathCalculator = new OrthogonalPathCalculator();
            conn.Source.ConnectedNode = (MyNode)node;
            conn.Source.ConnectorIndex = indexSource;
            conn.SourceText = "0..N";
            if (SinkText == "0..N")
            {
                conn.SourceDecorator = SD.Shapes.Common.Connections.LineEndDecoratorType.Arrow;

            }
            else
            {
                conn.SourceDecorator = SD.Shapes.Common.Connections.LineEndDecoratorType.None;

            }

            conn.Sink.ConnectedNode = (MyNode)itemEntity;
            conn.Sink.ConnectorIndex = indexSink;

            conn.SinkDecorator = SD.Shapes.Common.Connections.LineEndDecoratorType.Arrow;

            conn.SinkText = SinkText;
            var existscon = from e in _MyEntityList
                            where
                                e is MyConnection &&
                                ((MyConnection)e).MetadataId == metadataID 
                            select e;
            if (existscon.Count() == 0)
            {
                _MyEntityList.Add(conn);
            }
        }



        private RelayCommand _GenerateDiagramFromSolution;

        /// <summary>
        /// Gets the ClearEntities_Click.
        /// </summary>
        public RelayCommand GenerateDiagramFromSolution
        {
            get
            {
                return _GenerateDiagramFromSolution
                    ?? (_GenerateDiagramFromSolution = new RelayCommand(
                                          () =>
                                          {
                                              IsBusy = true;
                                              ObservableCollection<EntityClass> EntitySolutionList = new ObservableCollection<EntityClass>();
                                              EntitySolutionList = _dataService.GetEntitiesBySolution(SelectedSolution.SolutionId, (item, error) =>
                                              {
                                                  double staringPoint = 20;
                                                  if (EntitySolutionList.Count > 0)
                                                  {
                                                      DrawNodesFromSolution(EntitySolutionList, staringPoint);
                                                  }
                                                  else
                                                  {
                                                      IsBusy = false;
                                                  }
                                              });
                                              IsDirty = true;
                                          },
                                          () => { return (SelectedSolution != null); }));
            }
        }

        private RelayCommand _ClearEntities_Click;

        /// <summary>
        /// Gets the ClearEntities_Click.
        /// </summary>
        public RelayCommand ClearEntities_Click
        {
            get
            {
                return _ClearEntities_Click
                    ?? (_ClearEntities_Click = new RelayCommand(
                                          () =>
                                          {
                                              _MyEntityList.Clear();
                                              IsBusy = true;
                                              EntityList = _dataService.GetEntities((item, error) =>
                                              {
                                                  EntitiesOrig = new ObservableCollection<EntityClass>(from ee in EntityList select ee);
                                                  SelectedEntityType = EntityTypeList.First();
                                                  LayoutrootWidth = 600;
                                                  LayoutrootHeight = 600;

                                                  IsBusy = false;
                                              });
                                              IsDirty = true;
                                          }));
            }
        }
        ////public override void Cleanup()
        ////{
        ////    // Clean up if needed

        ////    base.Cleanup();
        ////}
    }
    #region Object Classes

    public class EntityTypeClass
    {
        public string TypeName { get; set; }
    }


    public class EntityClass : NotifyPropertyChangedBase
    {
        public EntityClass()
        {
            this.Attributes = new ObservableCollection<AttributeClass>();
            this.RemovedAttributes = new ObservableCollection<AttributeClass>();
            this.Relationships_OneToMany = new ObservableCollection<RelationshipClass>();
            this.Relationships_ManyToOne = new ObservableCollection<RelationshipClass>();
            this.Relationships_ManyToMany = new ObservableCollection<RelationshipClass>();
            InitialiseEvents();
        }

        public EntityClass(String SchemaNameSet, String LogicalName, bool? IsCustomEntity, Guid entityid)
        {
            this.EntityId = entityid;
            this.SchemaNameSet = SchemaNameSet;
            this.LogicalName = LogicalName;
            if (IsCustomEntity != null)
            {
                this.IsCustomEntity = (bool)IsCustomEntity;
            }
            this.Attributes = new ObservableCollection<AttributeClass>();
            this.RemovedAttributes = new ObservableCollection<AttributeClass>();
            this.Relationships_OneToMany = new ObservableCollection<RelationshipClass>();
            this.Relationships_ManyToOne = new ObservableCollection<RelationshipClass>();
            this.Relationships_ManyToMany = new ObservableCollection<RelationshipClass>();
            InitialiseEvents();

        }

        public string SchemaNameSet { get; set; }
        public string LogicalName { get; set; }
        public Guid EntityId { get; set; }
        public bool IsCustomEntity { get; set; }
        public bool IsManagedEntity { get; set; }

        private ObservableCollection<AttributeClass> _attributes;
        private ObservableCollection<AttributeClass> _removedattributes;


        private ObservableCollection<RelationshipClass> _relationships_OneToMany;
        private ObservableCollection<RelationshipClass> _relationships_ManyToOne;
        private ObservableCollection<RelationshipClass> _relationships_ManyToMany;



        private void InitialiseEvents()
        {
            //
            // Initialise Attribute selected events
            //
            _attributes.CollectionChanged += ((o, a) =>
            {
                NotifyCollectionChangedEventArgs args = (NotifyCollectionChangedEventArgs)a;

                if (args.Action == NotifyCollectionChangedAction.Add)
                {
                    AttributeClass item = (AttributeClass)args.NewItems[0];
                }
            });


        }



        public ObservableCollection<AttributeClass> Attributes
        {
            get { return _attributes; }
            set { if (_attributes != value) { _attributes = value; OnPropertyChanged(() => Attributes); } }
        }
        public ObservableCollection<AttributeClass> RemovedAttributes
        {
            get { return _removedattributes; }
            set { if (_removedattributes != value) { _removedattributes = value; OnPropertyChanged(() => RemovedAttributes); } }
        }

        public ObservableCollection<RelationshipClass> Relationships_OneToMany
        {
            get { return _relationships_OneToMany; }
            set { if (_relationships_OneToMany != value) { _relationships_OneToMany = value; OnPropertyChanged(() => Relationships_OneToMany); } }
        }

        public ObservableCollection<RelationshipClass> Relationships_ManyToOne
        {
            get { return _relationships_ManyToOne; }
            set { if (_relationships_ManyToOne != value) { _relationships_ManyToOne = value; OnPropertyChanged(() => Relationships_ManyToOne); } }
        }

        public ObservableCollection<RelationshipClass> Relationships_ManyToMany
        {
            get { return _relationships_ManyToMany; }
            set { if (_relationships_ManyToMany != value) { _relationships_ManyToMany = value; OnPropertyChanged(() => Relationships_ManyToMany); } }
        }

    }


    public partial class AttributeClass : NotifyPropertyChangedBase
    {
        public AttributeClass(AttributeMetadata Attribute, Guid entityId)
        {
            this.Metadata = Attribute;
            this.EntityId = entityId;

        }

        public Visibility LookupAttrVisible
        {
            get
            {
                if (this.Metadata is LookupAttributeMetadata)
                {
                    return Visibility.Visible;
                }
                else
                {
                    return Visibility.Collapsed;
                }
            }
        }
        public Visibility PrimaryKey
        {
            get
            {
                if (this.Metadata.IsPrimaryId != null && (bool)this.Metadata.IsPrimaryId == true)
                {
                    return Visibility.Visible;
                }
                else
                {
                    return Visibility.Collapsed;
                }
            }
        }
        public Guid EntityId { get; set; }
        public AttributeMetadata Metadata { get; set; }
        public string SchemaName { get { return Metadata.SchemaName; } }
        public string TargetRecordType { get; set; }
        public string DisplayName
        {
            get
            {
                if (Metadata.DisplayName.LocalizedLabels.Count > 0)
                    return Metadata.DisplayName.LocalizedLabels[0].Label;
                else
                    return "";
            }
        }
        public AttributeTypeCode? AttributeType { get { return Metadata.AttributeType; } }
        public string EntityLogicalName { get { return Metadata.EntityLogicalName; } }

        public event EventHandler IsAnyUnSelectedEvent;
        public event EventHandler IsSelectAllEvent;
        public event EventHandler IsUnSelectAllEvent;
        public string Type
        {
            get
            {
                return this.AttributeType.Value.ToString();
            }
        }
        public string Size
        {
            get
            {
                string maxlenght = "";
                if (this.AttributeType.Value == AttributeTypeCode.String)
                {
                    maxlenght = ((StringAttributeMetadata)this.Metadata).MaxLength.Value.ToString();
                }
                return maxlenght;
            }
        }

        private bool _isSelected;

        public string UserLocalizedLabel
        {
            get
            {
                string text = "";
                if ((Metadata.DisplayName != null))
                {
                    text = (Metadata.DisplayName.UserLocalizedLabel != null) ? Metadata.DisplayName.UserLocalizedLabel.Label : Metadata.SchemaName;
                }
                else
                {
                    text = Metadata.SchemaName;
                }

                return text;
            }
        }

        /// <summary>
        /// Allows for the selected option to be set without firing an event. (used for Select All action),
        /// </summary>
        public bool UpdateSelected
        {
            get { return _isSelected; }
            set { _isSelected = value; OnPropertyChanged(() => IsSelected); }
        }

        /// <summary>
        /// Is Selected property that fires events
        /// </summary>
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                _isSelected = value;
                OnPropertyChanged(() => IsSelected);
                if (_isSelected == false)
                {
                    if (SchemaName == "(Select All)")
                    {
                        IsUnSelectAllEvent(this, null);
                    }
                    else
                    {
                        IsAnyUnSelectedEvent(this, null);
                    }
                }
                else
                {
                    if (SchemaName == "(Select All)")
                    {
                        IsSelectAllEvent(this, null);
                    }
                }
            }
        }




    }

    public class RelationshipClass : NotifyPropertyChangedBase
    {
        public RelationshipClass(String SchemaName, String EntityLogicalName, Guid EntityRelationshipId, string primaryEntity)
        {
            this.SchemaName = SchemaName;
            this.EntityLogicalName = EntityLogicalName;
            this.EntityRelationshipId = EntityRelationshipId;
            this.PrimaryEntity = primaryEntity;

        }

        public Guid EntityRelationshipId { get; set; }
        public string SchemaName { get; set; }
        public string PrimaryEntity { get; set; }
        public string EntityLogicalName { get; set; }
        private EntityClass _entity;

        public EntityClass Entity
        {
            get { return _entity; }
            set
            {
                _entity = value;
                OnPropertyChanged(() => Entity);
            }
        }
    }

    [Serializable()]
    public class ERDDiagramFile
    {
        public double Width;
        public double Height;
        public ObservableCollection<SimpleNodes> Nodes;
        public ObservableCollection<SimpleConnection> Connections;

    }

    [Serializable()]
    public class SimpleNodes
    {
        public string EntityName { get; set; }
        public double Left { get; set; }
        public double Top { get; set; }
        public double Height { get; set; }
        public double Width { get; set; }
        public List<string> RemovedAttributes { get; set; }

    }
    [Serializable()]
    public class SimpleConnection
    {
        public int IndexSource { get; set; }
        public int indexSink { get; set; }
        public Guid MetadataId { get; set; }
    }

    #endregion


}